﻿<?php
/**
 * Zead
 *
 * LICENSE	
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/bsd-license.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to eu@marcelomx.com so we can send you a copy immediately.
 *
 * @category   Zead
 * @author     Marcelo Rodrigues Gonzaga <eu@marcelomx.com>
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga
 * @link       http://code.google.com/p/zead 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License
 *
 **/


/** @see Zead_Html_Element_Exception **/ 
require_once 'Zead/Xml/Element.php';

/** @see Zead_Html_ElementList **/
require_once 'Zead/Html/ElementList.php';

/**
 * Class Zead_Html_Element 
 * 
 * @category   Zead
 * @package    Zead_Html
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License   
 */
class Zead_Html_Element extends Zead_Xml_Element 
{
	/**
	 * Retorna as classes CSS utilizadas pelo elemento
	 * 
	 * @return array
	 */
	public function classNames() 
	{
		preg_match_all("/[^\s]+/", $this->getAttribute('class'), $classNames);
		return array_unique((array) $classNames[0]);
	}
	
	/**
	 * Verifica se  uma classe CSS existe no atributo class do elemento. Se existir
	 * retorna o índice dele na lista de classes disponíveis, do contrário, falso.
	 *
	 * @return mixed
	 */
	public function hasClassName($className) 
	{
		$className  = trim($className);
		foreach ($this->classNames() as $class)
			if ($class == $className) return true;

		return false;
	}
	
	/**
	 * Adiciona uma nova classe CSS ao atributo class do elemento.
	 *
	 * @param string $className
	 */
	public function addClassName($className) 
	{
		$classNames = $this->classNames();
		array_push($classNames, $className);
		$this->setAttribute('class', join(' ', $classNames));		
	}

	/**
	 * Remove uma classe CSS da lista de classes aplicadas ao elemento.
	 *
	 * @param string $className
	 */ 
	public function removeClassName($className) 
	{
		if (!is_int(($i = $this->hasClassName($className))))
			return;

		$classNames = $this->classNames();
		unset($classNames[$i]);
		
		$this->setAttribute('class', implode(' ', $classNames));
	}
	
	/**
	 * Seta os atributos do elemento. Deve ser passado o conjunto de
	 * atributos em um array associativo, com os nomes de atributos 
	 * como índices e seus valores, respectivamente.
	 *
	 * @param array $attributes
	 */
	public function setAttributes($attributes = array()) 
	{
		foreach ($attributes as $attribute => $value) {
			if (!is_string($value))
				continue;
			if ($attribute == 'class') {
				$this->addClassName($value);
				continue;
			}
			$this->setAttribute($attribute, $value);
		}
	}
	
	/**
	 * Procura por elementos a partir de uma string de seletores no padrão utilizado
	 * por folhas de estilo em cascata (CSS).
	 * 
	 * @param string $selector
	 * @return ArrayIterator
	 */
	public function select($selector) 
	{
		$pattern = "/(\w*|\*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[(\w+)(?:([!*^$]?=)[\"']?([^\"'\]]*)[\"']?)?])?/";		
		preg_match_all($pattern, $selector, $matches, PREG_SET_ORDER);
		$selectors = array();
		foreach ($matches as $match) {
			$key = null;
			$val = null;
			$tag = null;

			if ($match[0]=='') 
				continue;

			$tag = $match[1];

			if (!empty($match[2])) {
				$key = 'id';
				$val = $match[2];
			}
			if (!empty($match[3])) {
				$key = 'class';
				$val = $match[3];
			}
			if (!empty($match[4])) {
				$key = $match[4];
				if (!empty($match[6])) $val = $match[6];
			}
			$selectors[] = array(
				'tag' => $tag,
				'key' => $key,
				'val' => $val
			);
		}
		$result = array();
		$nodes  = array($this);

		if (($levels = count($selectors)) == 0)
			return array();

		for ($l = 0; $l < $levels; $l++) {
			$key = $selectors[$l]['key'];
			$val = $selectors[$l]['val'];
			$tag = $selectors[$l]['tag'];
			$result = array();
			
			// Seek
			foreach ($nodes as $node) {
				if ($tag == '') $tag = '*';
				$children = $node->getElementsByTagName($tag);

				for ($i = 0; $i < $children->length; $i++) {
					$child = &$children->item($i);
					
					if ($key == '') 
						$result[] = $child;					
					else if ($key == 'class' && $child->hasClassName($val) == true) 
						$result[] = $child;
					else if ($key != 'class' && $child->hasAttribute($key)) {
						$attr = $child->getAttribute($key);
						if ($attr == $val) $result[] = $child;
					}
				}
			}
			$nodes = $result;
		}

		return new Zead_Html_ElementList((array) $nodes);	
	}

	public function remove() {
		$this->parentNode->removeChild($this);
		return $this;
	}

	protected function innerHTML() 
	{}
	protected function outerHTML() 
	{}
}

